package obecto.task
{
	import flash.display.DisplayObject;
	import flash.events.Event;
	import flash.utils.Dictionary;
	
	import mx.messaging.SubscriptionInfo;
	
	import obecto.tween.AbstractTween;
	import obecto.tween.FrameTween;
	import obecto.tween.TimeTween;
	import obecto.tween.TweensManager;
	import obecto.utility.DictionaryUtil;

	public class PropertiesTweenAnimation extends BaseInterruptibleTask
	{
		private var _tween : AbstractTween;
		
		private var _target : Object;
		[Bindable]
		public function get target() : Object 
		{
			return _target;
		}
		public function set target(value : Object) : void
		{
			_target = value;
			if (_target is DisplayObject)
			{
				oldTargetCacheAsBitmatValue = (_target as DisplayObject).cacheAsBitmap;
			}
		}
		
		private var _duration : Number;
		[Bindable]
		public function set duration(value : Number) : void
		{
			_duration = value;
			roundTimeForFrameTween();
		}
		public function get duration() : Number
		{
			return _duration;
		}
		
		private var _tweenType : PropertiesTweenAnimationType;
		[Bindable]
		public function set tweenType(value : PropertiesTweenAnimationType) : void
		{
			_tweenType = value;
			roundTimeForFrameTween();
		}
		public function get tweenType() : PropertiesTweenAnimationType
		{
			return _tweenType;
		}
		
		[Bindable]
		public var tweeningProprties : Dictionary;
		[Bindable]
		public var startValues : Dictionary;
		
		private var gatheredStartValues : Dictionary;
		
		[Bindable]
		public var easingFunction : Function;
		
		private var oldTargetCacheAsBitmatValue : Boolean;
		private var targetCacheAsBitmapPolicyActual : AnimationTargetCacheAsBitmapPolicy;
		public var targetCacheAsBitmapPolicy : AnimationTargetCacheAsBitmapPolicy;
		
		private var _cuePoints:Array;
		
		public function PropertiesTweenAnimation()
		{
			super();
			
			_tweenType = PropertiesTweenAnimationType.FRAME_TWEEN;
			_cuePoints = new Array();
		}
		
		public function getCuePointEventNames() : Array 
		{
			var result : Array = new Array();
			
			for (var i : Number = 0; i < _cuePoints.length; i++)
			{
				result.push(_cuePoints[i].event);
			}
			
			return result;
		}
		
		public function addCuePoint(aPropertyValueReached:Number, aEventName:String) : void
		{
			_cuePoints.push({position:aPropertyValueReached, event:aEventName});
			_cuePoints.sortOn("position");
		}

		override protected function startActual() : void
		{
			determineActualSubjectCacheAsBitmapPolicy();
			setTargetCacheAsBitmapProperty();
			
			if(startValues != null)
			{
				applyPropertyValues(startValues);
			}
			else
			{
				gatheredStartValues = gatherPropertyValues(tweeningProprties);
			}
			
			createTween(tweeningProprties);
			TweensManager.instance.doTween(_tween);
		}		
		
		override protected function reverseActual() : void
		{
			var startProperties : Dictionary = 
				(gatheredStartValues) ? gatheredStartValues : startValues; 
			
			determineActualSubjectCacheAsBitmapPolicy();
			setTargetCacheAsBitmapProperty();
			
			createTween(startProperties);
			TweensManager.instance.doTween(_tween);
		}
		
		override public function interrupt() : void
		{
			TweensManager.instance.stopTween(_tween);
			super.interrupt();
		} 

		override public function end() : void
		{
			TweensManager.instance.stopTween(_tween);
			applyPropertyValues(tweeningProprties);
			
			super.end();
		} 

		override public function rollback() : void
		{
			var startProperties : Dictionary = 
				(gatheredStartValues) ? gatheredStartValues : startValues; 
				
			TweensManager.instance.stopTween(_tween);
			applyPropertyValues(startProperties);
			
			super.rollback();
		} 
		
		private function createTween(aTweeningProperties : Dictionary) : void
		{
			if (_tweenType == PropertiesTweenAnimationType.FRAME_TWEEN)
			{
				_tween = new FrameTween(target, aTweeningProperties);
			}
			else if (_tweenType == PropertiesTweenAnimationType.TIME_TWEEN)
			{
				_tween = new TimeTween(target, aTweeningProperties);
			}
			else
			{
				throw new Error("Invalid tween type.");
			}
			
			_tween.duration = duration;
			_tween.easingEquation = easingFunction;
			_tween.tweenUpdateDelegate = handleTweenUpdate;
			_tween.tweenFinishedDelegate = handleTweenFinished;
		}
		
		private function applyPropertyValues(propertyValues : Dictionary) : void
		{
			for(var key : String in propertyValues)
			{
				_target[key] = propertyValues[key];
			}
		}
		
		private function gatherPropertyValues(properyMatrix : Dictionary) : Dictionary
		{
			var result : Dictionary = new Dictionary();
			
			for(var key : String in properyMatrix)
			{
				result[key] = _target[key];
			}
			
			return result;
		}
		
		private function determineActualSubjectCacheAsBitmapPolicy() : void
		{
			if(targetCacheAsBitmapPolicy == AnimationTargetCacheAsBitmapPolicy.AUTO)
			{
				if(_target is DisplayObject)
				{
					if (DictionaryUtil.containsAnyKey(["x", "y"], tweeningProprties))
					{
						targetCacheAsBitmapPolicyActual = AnimationTargetCacheAsBitmapPolicy.FORCE_TRUE_WHILE_ANIMATING;
					}
					else
					{
						targetCacheAsBitmapPolicyActual = AnimationTargetCacheAsBitmapPolicy.LEAVE;
					}
					
					if (DictionaryUtil.containsAnyKey(["width", "height", "alpha", "scaleX", "scaleY", "rotation"], tweeningProprties))
					{
						targetCacheAsBitmapPolicyActual = AnimationTargetCacheAsBitmapPolicy.FORCE_FALSE_WHILE_ANIMATING;
					}
					else
					{
						targetCacheAsBitmapPolicyActual = AnimationTargetCacheAsBitmapPolicy.LEAVE;
					}
				}
				else
				{
					targetCacheAsBitmapPolicyActual = AnimationTargetCacheAsBitmapPolicy.LEAVE;
				}
			}
			else
			{
				targetCacheAsBitmapPolicyActual = targetCacheAsBitmapPolicy;
			}
		}
		
		private function setTargetCacheAsBitmapProperty() : void
		{
			if (_target is DisplayObject)
			{
				if(targetCacheAsBitmapPolicyActual == AnimationTargetCacheAsBitmapPolicy.FORCE_TRUE_WHILE_ANIMATING)
				{
					(_target as DisplayObject).cacheAsBitmap = true;
				}
				else if(targetCacheAsBitmapPolicyActual == AnimationTargetCacheAsBitmapPolicy.FORCE_FALSE_WHILE_ANIMATING)
				{
					(_target as DisplayObject).cacheAsBitmap = false;
				}
			}
		}
		
		private function revertTargetCacheAsBitmapPropert() : void
		{
			if (_target is DisplayObject && 
				targetCacheAsBitmapPolicy != AnimationTargetCacheAsBitmapPolicy.LEAVE)
			{
				(_target as DisplayObject).cacheAsBitmap = oldTargetCacheAsBitmatValue;
			}
		}

		private function roundTimeForFrameTween() : void
		{
			if (_tweenType == PropertiesTweenAnimationType.FRAME_TWEEN)
			{
				_duration = Math.round(_duration);	
			}
		}
		
		private function handleTweenUpdate() : void
		{
			if(_cuePoints.length > 0)
			{
				var currentPosition:Number = _target[tweeningProprties[0]];
				
				for(var i:Number = _cuePoints.length - 1; i >= 0; i--)
				{
					if(currentPosition >= _cuePoints[i].position)
					{
						dispatchEvent(new Event(_cuePoints[i].event));
						break;
					}
				}
			}
		}
		
		private function handleTweenFinished() : void
		{
			revertTargetCacheAsBitmapPropert();
			//mSnapshotableObjectsRegister.resetSnapshotableObjects();
			
			complete();
		}
	}
}